﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Transactions;
using YmtSystem.CrossCutting;
using IsolationLevel = System.Transactions.IsolationLevel;
using _IsolationLevel=System.Data.IsolationLevel;

namespace YmtAuth.Repository.DapperRepository
{
    /// <summary>
    /// 事务上下文
    /// </summary>
    public interface ITransactionContext : IDisposable
    {
        void Complete();
    }
    /// <summary>
    /// 链接上下文
    /// </summary>
    public class ConnectionContext : IDbConnection
    {
        public IDbTransaction Transaction { get; private set; }
        private IDbConnection connection;

        public ConnectionContext(IDbConnection conn)
        {
            YmtSystemAssert.AssertArgumentNotNull(conn, "IDbConnection can't null");
            this.connection = conn;
        }

        public IDbTransaction BeginTransaction(System.Data.IsolationLevel il)
        {
            return (this.Transaction = this.connection.BeginTransaction(il));
        }

        public IDbTransaction BeginTransaction()
        {
            return this.connection.BeginTransaction();
        }

        public void ChangeDatabase(string databaseName)
        {
           this.connection.ChangeDatabase(databaseName);
        }

        public void Close()
        {
            if(this.Transaction!=null)return;
            this.connection.Close();
        }

        public string ConnectionString
        {
            get { return this.connection.ConnectionString; }
            set { this.connection.ConnectionString = value; }
        }

        public int ConnectionTimeout
        {
            get { return this.connection.ConnectionTimeout; }
        }

        public IDbCommand CreateCommand()
        {
            return this.connection.CreateCommand();
        }

        public string Database
        {
            get { return this.connection.Database; }
        }

        public void Open()
        {
            if(this.State== ConnectionState.Closed)
                this.connection.Open();
        }      

        public ConnectionState State
        {
            get { return this.connection.State; }
        }   
        public void ForceDispose()
        {
            this.connection.Dispose();
        }

        public void Dispose()
        {
            if (this.Transaction != null) return;
            this.connection.Dispose();
        }
    }
    /// <summary>
    /// 事务上下文
    /// </summary>
    public class TransactionBaseContext : ITransactionContext
    {
        private TransactionScope scope;
        //SqlTransaction
        public TransactionBaseContext(TransactionScopeOption option = TransactionScopeOption.Required,
            IsolationLevel level = IsolationLevel.ReadCommitted)
        {
            this.scope = new TransactionScope(option, new TransactionOptions {IsolationLevel = level});
        }

        public void Complete()
        {
            this.scope.Complete();
        }

        public void Dispose()
        {
            this.scope.Dispose();
        }
    }
    /// <summary>
    /// 空事务
    /// </summary>
    internal class EmptyTransaction : ITransactionContext
    {
        private EmptyTransaction() { }

        public void Complete()
        {

        }

        public void Dispose()
        {

        }

        public readonly static ITransactionContext Instance = new EmptyTransaction();
    }

    /// <summary>
    /// 数据操作上下文
    /// </summary>
    public class _DbContext : ITransactionContext
    {
        private bool committed;       
        private ConnectionContext connection;
        private IDbTransaction transaction;
        internal bool Disposed { get; private set; }

        public _DbContext(ConnectionContext conn)
        {        
            this.connection = conn;
        }
        public IDbTransaction Transaction
        {
            get { return this.transaction; }
        }

        public ConnectionContext Connection
        {
            get { return this.connection; }           
        }

        public void Open()
        {
            this.connection.Open();
        }

        public void BeginTransaction(System.Data.IsolationLevel level)
        {
            this.transaction = this.connection.BeginTransaction(level);
        }
        public ITransactionContext NewTransaction()
        {
            return new TransactionBaseContext(System.Transactions.TransactionScopeOption.Required,
                System.Transactions.IsolationLevel.ReadCommitted);
        }
        public ITransactionContext NewTransaction(bool innerNestedTranRollbackEnabled,
           System.Transactions.IsolationLevel isolationLevel = System.Transactions.IsolationLevel.ReadCommitted)
        {
            if (!this.Disposed)
            {
                if (innerNestedTranRollbackEnabled)
                {
                    if (this.Connection == null || this.Connection.Transaction == null)
                    {
                        throw new InvalidOperationException("outer transaction should visit database once at least");
                    }

                    if (!(this.Connection.Transaction is SqlTransaction))
                    {
                        throw new NotSupportedException("SQL Server Only");
                    }

                    return new NestedStorageTransaction(this);
                }

                return EmptyTransaction.Instance;
            }

            return this;
        }

        public void Complete()
        {
            if (this.connection == null) throw new InvalidOperationException("Connection is null");

            this.transaction.Commit();
            this.committed = true;
        }

        public void Dispose()
        {
            if (this.connection == null) return;

            try
            {
                if (this.transaction != null && !this.committed)
                {
                    this.transaction.Rollback();
                }
            }
            finally
            {
                this.connection.ForceDispose();
                this.Disposed = true;
            }
        }
    }

    /// <summary>
    /// 通用数据库操作上下文
    /// </summary>
    public class _GeneralDbContext
    {       
        private _DbContext context;
        [ThreadStatic]
        private static _DbContext threadDbContext;
        private readonly Func<ConnectionContext> connFunc;

        public _GeneralDbContext(Func<ConnectionContext> connFunc)
        {
            this.connFunc = connFunc;            
        }

        public _DbContext Context(bool isBindThread)
        {
            if (!isBindThread) return new _DbContext(this.connFunc());
            if (threadDbContext == null)
                threadDbContext = new _DbContext(this.connFunc());
            return threadDbContext;
        }
    }

    /// <summary>
    /// 短暂的数据库操作上下文
    /// </summary>
    public class _TransitoryDbContext
    {
        private _DbContext context;
        private readonly Func<ConnectionContext> connFunc;
        public _TransitoryDbContext(Func<ConnectionContext> connFunc)
        {
            this.connFunc = connFunc;
        }
        public _DbContext Context
        {
            get { return new _DbContext(connFunc()); }
        }
    }
    /// <summary>
    /// 绑定线程的数据库操作上下文
    /// </summary>
    public class _ThreadDbContext
    {
        [ThreadStatic] private static _DbContext context;
        private readonly Func<ConnectionContext> connFunc;

        public _ThreadDbContext(Func<ConnectionContext> connFunc)
        {
            this.connFunc = connFunc;
        }

        public _DbContext Context
        {
            get
            {
                if (context == null)
                    context = new _DbContext(this.connFunc());
                return context;
            }
        }
    }

    internal class NestedStorageTransaction : ITransactionContext
    {
        public NestedStorageTransaction(_DbContext ambient)
        {
            if (ambient == null) throw new ArgumentNullException();
            if (ambient.Connection == null) throw new InvalidOperationException("No Connection");
            if (ambient.Connection.Transaction == null) throw new InvalidOperationException("No Transaction");
            if (!(ambient.Connection.Transaction is SqlTransaction)) throw new NotSupportedException("Sql Server Only");

            this.ambient = ambient;

            this.name = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

            (this.ambient.Connection.Transaction as SqlTransaction).Save(this.name);
        }

        public void Complete()
        {
            this.committed = true;
        }

        public void Dispose()
        {
            if (this.disposed) return;

            if (!this.committed)
            {
                (this.ambient.Connection.Transaction as System.Data.SqlClient.SqlTransaction).Rollback(this.name);
            }

            this.disposed = true;
        }

        public string Name
        {
            get { return this.name; }
        }

        private string name;
        private bool committed;
        private bool disposed;
        private _DbContext ambient;

        private int totoal;
    }
}
